use anyhow::Result;
use log::debug;
use log::info;
use regex::Regex;
use reqwest::header::*;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::str::FromStr;

use crate::REQWEST_CLIENT;

/// 'Referer': 'http://fund.eastmoney.com/data/fundranking.html'
///
/// Mapping to [FUND_RANKING.js](file:///Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/eastmoney/sample/FUND_RANKING.js)
///
/// Mapping to [FUND_RANKING.json](file:///Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/eastmoney/sample/FUND_RANKING.json)
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct FundRanking {
    #[serde(rename = "datas")]
    pub datas: Vec<String>, // 数据列表
    #[serde(rename = "allRecords")]
    pub all_records: u32, // 总记录数
    #[serde(rename = "pageIndex")]
    pub page_index: u32, // 当前页码
    #[serde(rename = "pageNum")]
    pub page_num: u32, // 每页记录数
    #[serde(rename = "allPages")]
    pub all_pages: u32, // 总页数
    #[serde(rename = "allNum")]
    pub all_num: Option<Value>, // 总数量
    #[serde(rename = "zs_count")]
    pub zs_count: Option<Value>, // 指数型基金数量
    #[serde(rename = "gp_count")]
    pub gp_count: Option<Value>, // 股票型基金数量
    #[serde(rename = "hh_count")]
    pub hh_count: Option<Value>, // 混合型基金数量
    #[serde(rename = "zq_count")]
    pub zq_count: Option<Value>, // 债券型基金数量
    #[serde(rename = "qdii_count")]
    pub qdii_count: Option<Value>, // QDII基金数量
    #[serde(rename = "fof_count")]
    pub fof_count: Option<Value>, // FOF基金数量
}
/// 基金代码| 基金简称 | 拼音缩写   |     日期	|       单位净值|	累计净值|	日增长率|	近1周|	近1月|	近3月|	近6月|	近1年|	近2年|	近3年|	今年来|	成立来|	成立日期|未知1|	自定义| 未知2|手续费|
/// --|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|
/// 000001|华夏成长混合|HXCZHH|2024-03-15|0.758|3.321|0.4|1.74|6.61|-3.93|-14.64|-20.63|-22.98|-35.18|-2.94|370.63|2001-12-18|1|-18.5822|1.50%|0.15%|1|0.15%|1|-21.25
#[derive(Debug, Deserialize, Default)]
pub struct FundData {
    pub code: String, // 基金代码
    pub name: String, // 基金简称
    pub pinyin_abbreviation: String, // 拼音缩写
    pub date: String, // 日期
    pub unit_net_value: f64, // 单位净值
    pub cumulative_net_value: f64, // 累计净值
    pub daily_growth_rate: f64, // 日增长率
    pub weekly_growth_rate: f64, // 近1周
    pub monthly_growth_rate: f64, // 近1月
    pub quarterly_growth_rate: f64, // 近3月
    pub half_yearly_growth_rate: f64, // 近6月
    pub yearly_growth_rate: f64, // 近1年
    pub two_years_growth_rate: f64, // 近2年
    pub three_years_growth_rate: f64, // 近3年
    pub year_to_date_growth_rate: f64, // 今年来
    pub since_inception_growth_rate: f64, // 成立来
    pub inception_date: String, // 成立日期
    pub unknown1: String, // 未知1
    pub custom: String, // 自定义
    pub unknown2: String, // 未知2
    pub fee: String, // 手续费
}

impl FundRanking {
    /// 开放基金排行
    /// for test: need to visit http://fund.eastmoney.com/data/fundranking.html and then
    ///    https://fund.eastmoney.com/data/rankhandler.aspx?op=ph&dt=kf&ft=all&rs=&gs=0&sc=1nzf&st=asc&sd=2023-02-26&ed=2024-02-26&qdii=&tabSubtype=,,,,,&pi=1&pn=50&dx=1&v=0.9453218642480823
    /// or else get: ```var rankData ={ErrCode:-999,Data:"无访问权限"}```
    ///
    /// date: e.g. 2023-03-18
    ///
    /// pi: page index, start from 1,
    ///
    /// pn: page size
    ///
    pub async fn fund_rankhandler(date: &str, pi: u16, pn: u16) -> Result<FundRanking> {
        // GET /data/rankhandler.aspx?op=ph&dt=kf&ft=all&rs=&gs=0&sc=dm&st=asc&sd=2023-03-18&ed=2024-03-18&qdii=&tabSubtype=,,,,,&pi=1&pn=50&dx=1&v=0.8210030079559432 HTTP/1.1
        // Accept: */*
        // Accept-Encoding: gzip, deflate, br, zstd
        // Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
        // Cache-Control: no-cache
        // Connection: keep-alive
        // Cookie: qgqp_b_id=d6299473145338f68687834ba37fa719; st_si=77687174285423; ASP.NET_SessionId=0dirn5vmkofeltudpaxdbyhu; AUTH_FUND.EASTMONEY.COM_GSJZ=AUTH*TTJJ*TOKEN; EMFUND0=01-29%2015%3A02%3A07@%23%24%u534E%u590F%u4E2D%u8BC1%u5168%u6307%u623F%u5730%u4EA7ETF@%23%24515060; EMFUND1=01-30%2014%3A41%3A22@%23%24%u534E%u6CF0%u67CF%u745E%u6D88%u8D39%u6210%u957F%u6DF7%u5408@%23%24001069; EMFUND2=01-30%2014%3A45%3A59@%23%24%u5E7F%u53D1%u6052%u751F%u6D88%u8D39%28QDII-ETF%29@%23%24159699; EMFUND3=01-30%2014%3A47%3A24@%23%24%u5357%u65B9%u7EA2%u5229%u4F4E%u6CE250ETF@%23%24515450; EMFUND4=01-31%2015%3A21%3A23@%23%24%u56FD%u8054%u4E2D%u8BC1%u7164%u70AD%u6307%u6570%28LOF%29A@%23%24168204; EMFUND5=02-02%2008%3A34%3A56@%23%24%u666F%u987A%u957F%u57CE%u652F%u67F1%u4EA7%u4E1A%u6DF7%u5408@%23%24260117; EMFUND6=02-02%2010%3A30%3A22@%23%24%u897F%u90E8%u5229%u5F97%u805A%u79BE%u6DF7%u5408A@%23%24007423; EMFUND7=02-26%2016%3A07%3A01@%23%24%u91D1%u9E70%u6DFB%u76C8%u7EAF%u503A%u503A%u5238A@%23%24003384; EMFUND8=02-27%2010%3A51%3A37@%23%24%u534E%u590F%u6210%u957F%u6DF7%u5408@%23%24000001; EMFUND9=02-27 13:53:07@#$%u534E%u6CF0%u67CF%u745E%u5357%u65B9%u4E1C%u82F1%u6052%u751F%u79D1%u6280%28QDII-ETF%29@%23%24513130; st_asi=delete; HAList=ty-0-300078-%u601D%u521B%u533B%u60E0%2Cty-1-600519-%u8D35%u5DDE%u8305%u53F0%2Cty-1-513130-%u6052%u751F%u79D1%u6280ETF%2Cty-0-300059-%u4E1C%u65B9%u8D22%u5BCC%2Cty-0-837046-%u4EBF%u80FD%u7535%u529B%2Cty-0-000001-%u5E73%u5B89%u94F6%u884C%2Cty-1-601360-%u4E09%u516D%u96F6%2Cty-1-600055-%u4E07%u4E1C%u533B%u7597%2Cty-0-159001-%u8D27%u5E01ETF%2Cty-0-161127-%u6807%u666E%u751F%u7269%u79D1%u6280LOF; st_pvi=47260449691882; st_sp=2024-01-12%2017%3A28%3A27; st_inirUrl=https%3A%2F%2Fwww.google.com.hk%2F; st_sn=2543; st_psi=20240318143423383-112200312936-9394338295
        // Host: fund.eastmoney.com
        // Pragma: no-cache
        // Referer: https://fund.eastmoney.com/data/fundranking.html
        // Sec-Fetch-Dest: script
        // Sec-Fetch-Mode: no-cors
        // Sec-Fetch-Site: same-origin
        // User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
        // sec-ch-ua: "Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"
        // sec-ch-ua-mobile: ?0
        // sec-ch-ua-platform: "macOS"

        // Query String Parameters
        //
        let op = "ph";
        let dt = "kf";
        let ft = "all";
        let rs = "";
        let gs = 0;
        let sc = "dm";
        let st = "asc";
        let sd = date; //"2023-03-18";
        let ed = date; //"2024-03-18";
        let qdii = "";
        let tabSubtype = ",,,,,";
        // let pi = 1;
        // let pn = 50000;
        let dx = 1;
        let v = 0.8210030079559432;

        let request_url = format!(
            "https://fund.eastmoney.com/data/rankhandler.aspx?op={op}&dt={dt}&ft={ft}&rs={rs}&gs={gs}&sc={sc}&st={st}&sd={sd}&ed={ed}&qdii={qdii}&tabSubtype={tabSubtype}&pi={pi}&pn={pn}&dx={dx}&v={v}"
        );
        info!("\n (fund_rankhandler) request_url= {}\n", request_url);

        let request = REQWEST_CLIENT.get(&request_url)
            .header(CONNECTION, "keep-alive")
            .header(
                USER_AGENT,
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75"
            )
            .header(ACCEPT, "*/*")
            .header(REFERER, "http://fund.eastmoney.com/data/fundranking.html")
            .header(ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
            .build()?;

        debug!("Request={request:?} \n\nurl= {}\n", request_url);

        let response = REQWEST_CLIENT.execute(request).await?;

        let body = response.text().await?;
        // debug!("body: {:?}", body);

        let json_body = body.replace("var rankData =", "").replace(";", "").replace("\\", "");

        let re = Regex::new(r#"(\w+):"#).unwrap();
        let json_str = re.replace_all(&json_body, r#""$1":"#);

        // println!("json_str: {}", json_str);

        let rank_data: FundRanking = serde_json::from_str(&json_str).unwrap();
        // debug!("\n\n{:?}", rank_data);

        Ok(rank_data)
    }

    /// 场内交易基金排行榜
    ///
    /// Refer : https://fund.eastmoney.com/data/fbsfundranking.html#tct;c0;r;sdm;dasc;pn50;
    ///
    /// Data: https://fund.eastmoney.com/data/rankhandler.aspx?op=ph&dt=fb&ft=ct&rs=&gs=0&sc=dm&st=asc&pi=1&pn=50&v=0.32035457745730755
    pub async fn fbsfundranking() -> Result<FundRanking> {
        // GET /data/rankhandler.aspx?op=ph&dt=fb&ft=ct&rs=&gs=0&sc=dm&st=asc&pi=1&pn=50&v=0.32035457745730755 HTTP/1.1
        // Accept: */*
        // Accept-Encoding: gzip, deflate, br, zstd
        // Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
        // Cache-Control: no-cache
        // Connection: keep-alive
        // Cookie: qgqp_b_id=d6299473145338f68687834ba37fa719; st_si=77687174285423; ASP.NET_SessionId=0dirn5vmkofeltudpaxdbyhu; AUTH_FUND.EASTMONEY.COM_GSJZ=AUTH*TTJJ*TOKEN; st_asi=delete; _adsame_fullscreen_18503=1; EMFUND0=01-31%2015%3A21%3A23@%23%24%u56FD%u8054%u4E2D%u8BC1%u7164%u70AD%u6307%u6570%28LOF%29A@%23%24168204; EMFUND1=02-02%2008%3A34%3A56@%23%24%u666F%u987A%u957F%u57CE%u652F%u67F1%u4EA7%u4E1A%u6DF7%u5408@%23%24260117; EMFUND2=02-02%2010%3A30%3A22@%23%24%u897F%u90E8%u5229%u5F97%u805A%u79BE%u6DF7%u5408A@%23%24007423; EMFUND3=02-26%2016%3A07%3A01@%23%24%u91D1%u9E70%u6DFB%u76C8%u7EAF%u503A%u503A%u5238A@%23%24003384; EMFUND4=03-18%2016%3A03%3A54@%23%24%u534E%u590F%u6210%u957F%u6DF7%u5408@%23%24000001; EMFUND5=03-18%2016%3A09%3A31@%23%24%u534E%u6CF0%u67CF%u745E%u5357%u65B9%u4E1C%u82F1%u6052%u751F%u79D1%u6280%28QDII-ETF%29@%23%24513130; EMFUND6=03-18%2016%3A07%3A12@%23%24%u6D77%u5BCC%u901A%u5F3A%u5316%u56DE%u62A5%u6DF7%u5408@%23%24519007; EMFUND7=03-18%2016%3A09%3A27@%23%24%u56FD%u6CF0%u79D1%u521B%u677F%u4E24%u5E74%u5B9A%u671F%u5F00%u653E%u6DF7%u5408@%23%24506009; EMFUND8=03-18%2016%3A16%3A59@%23%24%u4E1C%u8D22%u6570%u5B57%u7ECF%u6D4E%u6DF7%u5408%u53D1%u8D77%u5F0FC@%23%24015097; EMFUND9=03-18 16:22:04@#$%u6C47%u6DFB%u5BCC%u9999%u6E2F%u4F18%u52BF%u7CBE%u9009%u6DF7%u5408%28QDII%29C@%23%24017873; HAList=ty-0-300078-%u601D%u521B%u533B%u60E0%2Cty-116-01357-%u7F8E%u56FE%u516C%u53F8%2Cty-1-513130-%u6052%u751F%u79D1%u6280ETF%2Cty-1-600519-%u8D35%u5DDE%u8305%u53F0%2Cty-0-300059-%u4E1C%u65B9%u8D22%u5BCC%2Cty-0-837046-%u4EBF%u80FD%u7535%u529B%2Cty-0-000001-%u5E73%u5B89%u94F6%u884C%2Cty-1-601360-%u4E09%u516D%u96F6%2Cty-1-600055-%u4E07%u4E1C%u533B%u7597%2Cty-0-159001-%u8D27%u5E01ETF; st_pvi=47260449691882; st_sp=2024-01-12%2017%3A28%3A27; st_inirUrl=https%3A%2F%2Fwww.google.com.hk%2F; st_sn=2570; st_psi=20240318162849111-113200301201-7702665568
        // Host: fund.eastmoney.com
        // Pragma: no-cache
        // Referer: https://fund.eastmoney.com/data/fbsfundranking.html
        // Sec-Fetch-Dest: script
        // Sec-Fetch-Mode: no-cors
        // Sec-Fetch-Site: same-origin
        // User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
        // sec-ch-ua: "Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"
        // sec-ch-ua-mobile: ?0
        // sec-ch-ua-platform: "macOS"

        // Query String Parameters
        //
        let op = "ph";
        let dt = "fb";
        let ft = "ct";
        let rs = "";
        let gs = 0;
        let sc = "dm";
        let st = "asc";
        let pi = 1;
        let pn = 50000;
        let v = 0.32035457745730755;

        let url = format!(
            "https://fund.eastmoney.com/data/rankhandler.aspx?op={op}&dt={dt}&ft={ft}&rs={rs}&gs={gs}&sc={sc}&st={st}&pi={pi}&pn={pn}&v={v}"
        );
        let request = REQWEST_CLIENT.get(url)
            .header(CONNECTION, "keep-alive")
            .header(
                USER_AGENT,
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75"
            )
            .header(ACCEPT, "*/*")
            .header(HOST, "fund.eastmoney.com")
            .header(REFERER, "https://fund.eastmoney.com/data/fbsfundranking.html")
            .header(ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
            .build()?;

        println!("Request={request:?} \n\nurl= {}\n", request.url().as_str());

        let response = REQWEST_CLIENT.execute(request).await?;

        let body = response.text().await?;
        // println!("body: {:?}", body);

        let json_body = body.replace("var rankData =", "").replace(";", "").replace("\\", "");

        let re = Regex::new(r#"(\w+):"#).unwrap();
        let json_str = re.replace_all(&json_body, r#""$1":"#);

        // println!("json_str: {}", json_str);

        let rank_data: FundRanking = serde_json::from_str(&json_str).unwrap();
        // println!("\n\n{:?}", rank_data);

        Ok(rank_data)
    }

    pub fn datas(self) -> Vec<FundData> {
        let fund_data_vec: Vec<FundData> = self.datas
            .iter()
            .map(|item| {
                let parts: Vec<&str> = item.split(',').collect();
                FundData {
                    code: parts[0].to_string(),
                    name: parts[1].to_string(),
                    pinyin_abbreviation: parts[2].to_string(),
                    date: parts[3].to_string(),
                    unit_net_value: f64::from_str(parts[4]).unwrap_or(0.0),
                    cumulative_net_value: f64::from_str(parts[5]).unwrap_or(0.0),
                    daily_growth_rate: f64::from_str(parts[6]).unwrap_or(0.0),
                    ..Default::default()
                }
            })
            .collect();

        fund_data_vec
    }
}

#[cfg(test)]
mod test {
    use crate::fund::funds_ranking::FundRanking;
    use regex::Regex;

    #[tokio::test]
    async fn test_fund_rankhandler() {
        let pn = 20;
        let result = FundRanking::fund_rankhandler("2024-03-15", 1, pn).await;

        assert_eq!(result.is_ok(), true);
        if let Ok(fund_rank_data) = result {
            assert_eq!(
                fund_rank_data.clone().all_num.unwrap_or_default().as_u64().unwrap_or_default() > 1,
                true
            );
            // println!("fund_rank_data.datas={:?}\n", fund_rank_data.datas);

            if let Some(first) = fund_rank_data.clone().datas().get(0) {
                assert_eq!(first.code, "000001");
            }

            assert_eq!(pn as usize, fund_rank_data.clone().datas().len());
            println!("fund_datas[0]={:?}", fund_rank_data.datas().get(0));
        }
    }

    #[tokio::test]
    async fn test_fbsfundranking() {
        let result = FundRanking::fbsfundranking().await;

        assert_eq!(result.is_ok(), true);
        if let Ok(fund_rank_data) = result {
            if let Some(first) = fund_rank_data.clone().datas().get(0) {
                assert_eq!(first.code, "159150");
            }

            assert_eq!(fund_rank_data.all_records as usize, fund_rank_data.clone().datas().len());
            println!("fund_datas[0]={:?}\n", fund_rank_data.clone().datas().get(0));

            // Find the FundData item with code "513130"
            if
                let Some(found_item) = fund_rank_data
                    .datas()
                    .iter()
                    .find(|&item| item.code == "513130")
            {
                println!("Found ETF FundData item: {:?}", found_item);
            } else {
                println!("FundData ETF item with code '513130' not found.");
            }
        }
    }

    #[tokio::test]
    async fn test_parse_fund_rank_data() {
        let data =
            r#"var rankData = {datas:[\"006401,先锋量化优选混合A,XFLHYXHHA,6151.727787,,0,2019-05-15,1,1,2024-02-23,1.3376,62.5173,2019-05-15,1,0.15%,1,0.15%,1,1.50%\",\"000011,华夏大盘精选混合A,HXDPJXHHA,2521.829081,7.1557,21,2004-08-11,1,1,2024-02-23,12.617,19.773,2004-08-11,1,0.15%,1,0.15%,1,1.50%\",\"070002,嘉实增长混合,JSZZHH,1838.961751,0.671,18,2003-07-09,1,1,2024-02-23,14.2414,14.9124,2003-07-09,4,0.15%,1,0.15%,1,1.50%\"],allRecords:19372,pageIndex:1,pageNum:3,allPages:6458,allNum:19372,etf_count:809,zs_count:3498,gp_count:956,hh_count:8111,zq_count:5618,qdii_count:306,fof_count:880};"#;

        let re = Regex::new(r#"(\w+):"#).unwrap();
        let converted_str = re
            .replace_all(&data, r#""$1":"#)
            .replace("var rankData =", "")
            .replace(";", "")
            .replace("\\", "");

        println!("converted_str: {}", converted_str);

        let rank_data: FundRanking = serde_json::from_str(&converted_str).unwrap();
        println!("{:?}", rank_data);
    }
}
